#include "defines.h"
#include "lib.h"
#include "kozos.h"
#include "softvec.h"
#include "nicdrv.h"
#include "ni.h"

#define H8_ISCR ((volatile uint8 *) 0xfee014)
#define H8_IER  ((volatile uint8 *) 0xfee015)
#define H8_ISR  ((volatile uint8 *) 0xfee016)

#define REGADDR 0x200000
#define CR      ((volatile uint8 *) REGADDR + 0x00)
#define RDMAP   ((volatile uint8 *) REGADDR + 0x10)
#define RP      ((volatile uint8 *) REGADDR + 0x18)

// Page 1
#define PSTART  ((volatile uint8 *) REGADDR + 0x01)
#define PSTOP   ((volatile uint8 *) REGADDR + 0x02)
#define BNRY    ((volatile uint8 *) REGADDR + 0x03)
#define TPSR    ((volatile uint8 *) REGADDR + 0x04)
#define TBCR0   ((volatile uint8 *) REGADDR + 0x05)
#define TBCR1   ((volatile uint8 *) REGADDR + 0x06)
#define ISR     ((volatile uint8 *) REGADDR + 0x07)
#define RSAR0   ((volatile uint8 *) REGADDR + 0x08)
#define RSAR1   ((volatile uint8 *) REGADDR + 0x09)
#define RBCR0   ((volatile uint8 *) REGADDR + 0x0a)
#define RBCR1   ((volatile uint8 *) REGADDR + 0x0b)
#define RCR     ((volatile uint8 *) REGADDR + 0x0c)
#define TCR     ((volatile uint8 *) REGADDR + 0x0d)
#define DCR     ((volatile uint8 *) REGADDR + 0x0e)
#define IMR     ((volatile uint8 *) REGADDR + 0x0f)

// Page 2
#define PAR0    ((volatile uint8 *) REGADDR + 0x01)
#define PAR1    ((volatile uint8 *) REGADDR + 0x02)
#define PAR2    ((volatile uint8 *) REGADDR + 0x03)
#define PAR3    ((volatile uint8 *) REGADDR + 0x04)
#define PAR4    ((volatile uint8 *) REGADDR + 0x05)
#define PAR5    ((volatile uint8 *) REGADDR + 0x06)
#define CURR    ((volatile uint8 *) REGADDR + 0x07)
#define MAR0    ((volatile uint8 *) REGADDR + 0x08)
#define MAR1    ((volatile uint8 *) REGADDR + 0x09)
#define MAR2    ((volatile uint8 *) REGADDR + 0x0a)
#define MAR3    ((volatile uint8 *) REGADDR + 0x0b)
#define MAR4    ((volatile uint8 *) REGADDR + 0x0c)
#define MAR5    ((volatile uint8 *) REGADDR + 0x0d)
#define MAR6    ((volatile uint8 *) REGADDR + 0x0e)
#define MAR7    ((volatile uint8 *) REGADDR + 0x0f)

#define TP_START    0x40
#define RP_START    0x46
#define RP_STOP     0x60

#define FRAME_MINSIZE   60
#define FRAME_MAXSIZE   1514

typedef struct {
    kz_msgbox_id_t mb;
    int priority;
} nic_use_header;

typedef struct _nic_send_header {
    uint8 *buf;
    int size;
    struct _nic_send_header *next;
} nic_send_header;

static kz_msgbox_id_t nicdrv_mbox;
static uint8 MAC[6];
static int send_state; // 0: 's'を待ってない。1: 's'を待っている、nicdrv_mboxにない。2: 's'を待っている、nicdrv_mboxにある。

static void nic_init(void);
static void nic_intr(void);

void nicdrv_init(void)
{
    nicdrv_mbox = kz_getmsgbox();
    send_state = 0;
    kz_setintr(SOFTVEC_TYPE_ETHINTR, nic_intr);
    nic_init();
}

void nic_MAC(uint8 *buffer)
{
    memcpy(buffer, MAC, 6);
}

void nic_send(uint8 *buffer, int size)
{
    nic_send_header *p = kz_kmalloc(sizeof(nic_send_header));
    p->buf = buffer;
    p->size = size;
    if (kz_send(nicdrv_mbox, 'w', (char *)p, 4, NET_MBOXSIZE) == -1) {
        kz_kmfree(buffer);
        kz_kmfree(p);
    }
}

static void nic_intr()
{
    uint8 f, x;

    f = *CR & 0xc0;
    if (f) {
        // ページ1の場合は限る(DMA中のページ切替は危ない)
        ASSERT(*CR == 0x62 || *CR == 0x66, "unexpected CR");
        *CR = 0x22;
    }

    x = *ISR & 0x1f; *ISR = x;

    if (x & 0x10) {
        *H8_IER = 0;
        kz_send(nicdrv_mbox, 'o', NULL, 1, 1);
    } else {
        if (x & (0x01 | 0x04)) kz_send(nicdrv_mbox, 'r', NULL, 2, 1);
        if (x & 0x04) puts("\nNICRECVERR\n"); /* 見たことない */
        if (x & (0x02 | 0x08)) {
            ASSERT(send_state == 1, "send_state error");
            kz_send(nicdrv_mbox, 's', NULL, 3, 1);
            send_state = 2;
        }
        if (x & 0x08) puts("\nNICSENDERR\n"); /* 見たことない */
    }

    if (f) *CR = 0x62;
}

static void nic_init()
{
    int i;
    uint8 x;

    x = *RP;
    *RP = x;

    kz_wait(TICK_PER_SEC / 100 + 1); // >= 10ms待つ

    *CR = 0x21;
    *DCR = 0x4a;
    *RBCR0 = 0;
    *RBCR1 = 0;
    *RCR = 0x20;
    *TCR = 0x02;
    *TPSR = TP_START;
    *PSTART = RP_START;
    *BNRY = RP_START;
    *PSTOP = RP_STOP;
    *IMR = 0;
    *ISR = 0xff;

    // MACの読み込み。
    *RBCR0 = 12;
    *RBCR1 = 0;
    *RSAR0 = 0;
    *RSAR1 = 0;
    *CR = 0x0a;
    for (i = 0; i < 6; i++) {
        MAC[i] = *RDMAP;
        x = *RDMAP;
    }

    *CR = 0x61;
    *PAR0 = MAC[0];
    *PAR1 = MAC[1];
    *PAR2 = MAC[2];
    *PAR3 = MAC[3];
    *PAR4 = MAC[4];
    *PAR5 = MAC[5];

    *CURR = RP_START + 1;

    *MAR0 = 0;
    *MAR1 = 0;
    *MAR2 = 0;
    *MAR3 = 0;
    *MAR4 = 0;
    *MAR5 = 0;
    *MAR6 = 0;
    *MAR7 = 0;

    *CR = 0x21;
    *RCR = 0x04;

    *IMR = 0x1f;
    *H8_ISCR = 0; // レベル(0)の方がなぜか安定。エッジ(0x20)は割込を取りこぼすことがあるようだ。
    *H8_IER = 0x20;

    *CR = 0x22;
    *TCR = 0;
}

static int frame_send(uint8 *buffer, uint16 size)
{
    uint16 i;
    uint8 size_h, size_l;

    if (size > FRAME_MAXSIZE) return -1;

    size_l = (uint8)size;
    size_h = (uint8)(size >> 8);

    // バッファへ転送
    *CR = 0x22;
    *RBCR0 = size_l;
    *RBCR1 = size_h;
    *RSAR0 = 0;
    *RSAR1 = TP_START;
    *ISR = 0x40;
    *CR = 0x12;
    for (i = 0; i < size; i++) {
        *RDMAP = buffer[i];
    }
    while (!(*ISR & 0x40))
        ;

    // 実際の送信
    *CR = 0x22;
    *TBCR0 = (size > FRAME_MINSIZE) ? size_l : FRAME_MINSIZE;
    *TBCR1 = size_h;
    *TPSR = TP_START;
    *CR = 0x26;

    return 0;
}

static int frame_receive(uint8 *buffer) // bufferは1514バイト書き込めるとする
{
    uint16 i, size;
    uint8 x, start;
    uint8 header[4];

    *CR = 0x22;
    x = *BNRY;
    start = (x == RP_STOP - 1) ? RP_START : x + 1;

    *CR = 0x62;
    if (*CURR == start) { // 受信してない
        return -1;
    }

    *CR = 0x22;
    *RBCR0 = 4;
    *RBCR1 = 0;
    *RSAR0 = 0;
    *RSAR1 = start;
    *CR = 0x0a;
    for (i = 0; i < 4; i++) {
        header[i] = *RDMAP;
    }

    if (!(header[0] & 0x01)) { // 異常受信
        return -2;             // 受信オーバーランとみなして一旦リセットしたほうが良いようだ
    }

    size = ((uint16)header[3] << 8) + header[2] - 4;
    if (size < FRAME_MINSIZE || size > FRAME_MAXSIZE) { // 受信しないが続けて受信できるかもしれない
        size = 0;
        goto refresh_bnry;
    }

    *CR = 0x22;
    *RBCR0 = (uint8)size;
    *RBCR1 = (uint8)(size >> 8);
    *RSAR0 = 4;
    *RSAR1 = start;
    *CR = 0x0a;
    for (i = 0; i < size; i++) {
        buffer[i] = *RDMAP;
    }

refresh_bnry:
    *CR = 0x22;
    *BNRY = (header[1] == RP_START) ? RP_STOP - 1 : header[1] - 1;

    return size;
}

static void overrun()
{
    while (*CR & 0x04)
        ;
    *CR = 0x21;
    kz_wait(TICK_PER_SEC / 625 + 1); // >= 1.6ms
    *RBCR0 = 0;
    *RBCR1 = 0;
    *TCR = 0x02;
    *BNRY = RP_START;
    *CR = 0x61;
    *CURR = RP_START + 1;
    *CR = 0x21;
    *ISR = 0xff;
    *H8_IER = 0x20;
    *CR = 0x22;
    *TCR = 0;
}

void nicdrv_main(int argc, char *argv)
{
    nic_send_header *head = NULL, *tail = NULL;
    uint8 *buf = kz_kmalloc(FRAME_MAXSIZE);

    ASSERT(ni_mbox != MSGBOX_NULL, "no ni_mbox");

    while (1) {
        int cmd;
        char *p;
        kz_recv(nicdrv_mbox, &cmd, &p);

        ASSERT(!(head && send_state == 0), "head && send_state == 0");

        if (cmd == 'r') { // フレーム受信
            int size;
            while ((size = frame_receive(buf)) >= 0) {
                if (size > 0) {
                    if (kz_send(ni_mbox, size, (char *)buf, 1, NET_MBOXSIZE) == 0)
                        buf = kz_kmalloc(FRAME_MAXSIZE);
                }
            }
            if (size == -2) {
                *H8_IER = 0;
                kz_send(nicdrv_mbox, 'o', NULL, 1, 1);
            }
        } else if (cmd == 's') { // 送信可能
            ASSERT(send_state == 2, "unexpected send_state");
            send_state = 0;
            if (head) {
                nic_send_header *q = head;
                head = q->next;
                if (!head) tail = NULL;
                send_state = 1;
                if (frame_send(q->buf, q->size) == -1) {
                    kz_send(nicdrv_mbox, 's', NULL, 3, 1);
                    send_state = 2;
                }
                kz_kmfree(q->buf);
                kz_kmfree(q);
            }

        } else if (cmd == 'w') { // 送信要求
            nic_send_header *q = (nic_send_header *)p;

            if (send_state == 0) {
                send_state = 1;
                if (frame_send(q->buf, q->size) == -1) {
                    kz_send(nicdrv_mbox, 's', NULL, 3, 1);
                    send_state = 2;
                }
                kz_kmfree(q->buf);
                kz_kmfree(q);
            } else {
                if (head) {
                    tail->next = q;
                } else {
                    head = q;
                }
                tail = q;
                q->next = NULL;
            }

        } else if (cmd == 'o') { // 受信オーバーラン
            // puts("\nNICOVERRUN\n");
            overrun();
            if (send_state == 1) {
                kz_send(nicdrv_mbox, 's', NULL, 3, 1);
                send_state = 2;
            }

        } else {
            ASSERT(0, "invalid cmd");
        }
    }

    ASSERT(0, "nic_main exit");
}
